package com.tl.boot.main.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tl.boot.common.constants.RequestConstant;
import com.tl.boot.common.enums.ResultEnum;
import com.tl.boot.common.utils.LogUtil;
import com.tl.boot.entity.common.TokenUtil;
import com.tl.boot.common.vo.ResponseVO;
import com.tl.boot.common.wrapper.DecorateHttpServletRequestWrapper;
import com.tl.boot.entity.common.Token;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * com.tl.boot.main.filter -- PreRequestFilter
 * Description:
 * Created by joshua_liu <a href="mailto:joshualwork@163.com"></a>
 * On 2019/9/11 8:33
 */
@Order(FilterRegistrationBean.HIGHEST_PRECEDENCE+1000)
@WebFilter(filterName = "preRequestFilter", urlPatterns = {"/*"})
public class PreRequestFilter implements Filter {
    private final String WRAPPER_LOG_FORMAT = "requestMethod:%s>>>requestURI:%s>>>headers:%s>>>requestParams:%s>>>response:%s";
    private final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";

    private final String XML_CONTENT_TYPE = "text/xml";


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        DecorateHttpServletRequestWrapper requestWrapper = new DecorateHttpServletRequestWrapper(request);
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        initCors(request,response);
        String contentType = requestWrapper.getContentType();
        String url = requestWrapper.getRequestURI();
        String method = requestWrapper.getMethod();
        JSONObject headerValues = requestWrapper.getHeaders();
        //传参校验
        //解析token
        String encryptToken = (String) headerValues.get(RequestConstant.HEADER_TOKEN_NAME);
        Token requestToken = null;
        //如果token不为空，进行token校验
        if (StringUtils.isNotBlank(encryptToken)) {
            requestToken = TokenUtil.decrypt(encryptToken);
            ResultEnum errorEnum = TokenUtil.validate(requestToken);
            if (errorEnum != null) {
                processFailResponse(requestWrapper, response, ResponseVO.buildFail(errorEnum));
                return;
            }
            String redisEncryptToken = TokenUtil.getEncryptToken(requestToken.getUserId());
            Token redisToken = TokenUtil.decrypt(redisEncryptToken);
            errorEnum = TokenUtil.validate(redisToken);
            if (!encryptToken.equals(redisEncryptToken)) {
                errorEnum = ResultEnum.AUTH_NOTLOGIN;
            }
            if (errorEnum != null) {
                processFailResponse(requestWrapper, response, ResponseVO.buildFail(errorEnum));
                return;
            }
        }
        //如果token为空，判断是否为必须传token的接口，
        if (isAuthRequest(url) && requestToken == null) {
            ResultEnum error = StringUtils.isEmpty(encryptToken) ? ResultEnum.AUTH_NOTLOGIN:ResultEnum.REQUEST_ILLEGAL;
            processFailResponse(requestWrapper,response,ResponseVO.buildFail(error));
        }
        //参数重写
        if (RequestConstant.REQUEST_METHOD_POST.equals(method)) {
            LogUtil.info(String.format(WRAPPER_LOG_FORMAT,requestWrapper.getMethod(), requestWrapper.getRequestURI(),
                    requestWrapper.getHeaders().toJSONString(), requestWrapper.getParams(), ""));

            if (!XML_CONTENT_TYPE.equals(contentType)) {
                if (FORM_CONTENT_TYPE.equals(contentType)) {
                    if (requestToken != null) {
                        requestWrapper.addParameterValue("userId", requestToken.getUserId().toString());
                    }
                } else {
                    String body = requestWrapper.getBody();
                    JSONObject jsonObject = StringUtils.isEmpty(body) ? new JSONObject() : JSON.parseObject(body);
                    if (requestToken != null) {
                        jsonObject.put("userId", requestToken.getUserId());
                    }
                    requestWrapper.modifyBody(jsonObject.toString().getBytes());
                }
            }
        } else if (RequestConstant.REQUEST_METHOD_GET.equals(method)) {
            LogUtil.info(String.format(WRAPPER_LOG_FORMAT,requestWrapper.getMethod(), requestWrapper.getRequestURI(),
                    requestWrapper.getHeaders().toJSONString(),requestWrapper.getQueryString(),""));
            if (requestToken != null) {
                requestWrapper.addParameterValue("userId", requestToken.getUserId().toString());
            }
        } else {
            if (RequestConstant.REQUEST_METHOD_OPTIONS.equals(method)) {
                response.setStatus(HttpServletResponse.SC_NO_CONTENT);
            } else {
                processFailResponse(requestWrapper, response, ResponseVO.buildFail(ResultEnum.REQUEST_ILLEGAL));
            }

            return;
        }

        filterChain.doFilter(requestWrapper, servletResponse);
    }

    @Override
    public void destroy() {

    }

    private void initCors(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", String.format("%s, %s, %s", RequestConstant.REQUEST_METHOD_POST, RequestConstant.REQUEST_METHOD_GET, RequestConstant.REQUEST_METHOD_OPTIONS));
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, Content-Disposition, X-E4M-With, Authorization, " + RequestConstant.HEADER_TOKEN_NAME);
    }

    private boolean isAuthRequest(String url) {
        return url.toLowerCase().contains(RequestConstant.AUTH_KEY);
    }


    private void processFailResponse(DecorateHttpServletRequestWrapper requestWrapper, HttpServletResponse response, ResponseVO responseVO) {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType(RequestConstant.RESPONSE_CONTENT_TYPE);
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.append(responseVO.toString());
        } catch (IOException e) {
            LogUtil.error(String.format(WRAPPER_LOG_FORMAT,
                    requestWrapper.getMethod(), requestWrapper.getRequestURI(),requestWrapper.getHeaders().toJSONString(),
                    requestWrapper.getParams(), response));
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }
}
